package com.example.shuiyin.service.impl;

import com.example.shuiyin.config.MinioConfig;
import com.example.shuiyin.exception.AppException;
import com.example.shuiyin.exception.ErrorCode;
import com.example.shuiyin.service.MinioStorageService;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.beans.factory.annotation.Autowired;
import com.example.shuiyin.service.FileService;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.apache.commons.io.FilenameUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import io.minio.http.Method;
import io.minio.GetPresignedObjectUrlArgs;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Service
@Slf4j
public class MinioStorageServiceImpl implements MinioStorageService {

    private final MinioClient minioClient;
    private final MinioConfig minioConfig;
    
    @Autowired
    private FileService fileService;
    
    @Value("${server.domain:http://localhost:8080}")
    private String serverDomain;

    @Value("${minio.endpoint:http://localhost:9000}")
    private String minioEndpoint;
    
    @Value("${minio.bucket:shuiyin}")
    private String minioBucket;
    
    @Value("${minio.accessKey:}")
    private String accessKey;

    @Value("${minio.secretKey:}")
    private String secretKey;

    @Value("${minio.enabled:true}")
    private boolean minioEnabled;
    
    @Value("${storage.local.path:uploads}")
    private String localStoragePath;

    // 添加缓存
    private Map<String, CachedPresignedUrl> urlCache = new ConcurrentHashMap<>();

    // 封装带过期时间的URL
    private static class CachedPresignedUrl {
        private final String url;
        private final long expiryTime;
        
        public CachedPresignedUrl(String url, long expiryTimeMs) {
            this.url = url;
            this.expiryTime = expiryTimeMs;
        }
        
        public boolean isValid() {
            return System.currentTimeMillis() < expiryTime - 60000; // 提前1分钟过期
        }
        
        public String getUrl() {
            return url;
        }
    }

    public MinioStorageServiceImpl(MinioClient minioClient, MinioConfig minioConfig) {
        this.minioClient = minioClient;
        this.minioConfig = minioConfig;
        log.info("初始化MinioStorageService: 配置状态enabled={}, endpoint={}, accessKey={}, bucket={}",
                 minioConfig.isEnabled(), minioConfig.getEndpoint(), 
                 minioConfig.getAccessKey(), minioConfig.getBucket());
        
        try {
            if (minioConfig.isEnabled()) {
                log.info("MinIO客户端初始化成功: {}", minioConfig.getEndpoint());
                
                // 尝试立即进行连接测试
                boolean connected = testConnection();
                log.info("MinIO连接测试结果: {}", connected ? "成功" : "失败");
            } else {
                log.info("MinIO功能已禁用，将使用本地文件存储");
            }
        } catch (Exception e) {
            log.error("MinIO客户端初始化失败，将使用本地文件存储: {}", e.getMessage(), e);
            this.minioEnabled = false;
        }
    }

    /**
     * 确保MinioClient可用
     * @return 如果MinioClient可用返回true，否则返回false
     */
    private boolean ensureMinioClient() {
        if (!minioEnabled) {
            return false;
        }
        
        if (minioClient == null) {
            log.error("MinIO客户端为null，无法使用");
            minioEnabled = false;
            return false;
        }
        
        return true;
    }

    @Override
    public String uploadFile(MultipartFile file, String objectName) throws IOException {
        return uploadFile(file, minioConfig.getBucket(), objectName);
    }

    @Override
    public String uploadFile(MultipartFile file, String bucketName, String objectName) throws IOException {
        if (!ensureMinioClient()) {
            return uploadToLocalStorage(file, objectName);
        }
        
        try {
            // 检查存储桶是否存在，不存在则创建
            boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder()
                    .bucket(bucketName)
                    .build());
                    
            if (!bucketExists) {
                minioClient.makeBucket(MakeBucketArgs.builder()
                        .bucket(bucketName)
                        .build());
                
                // 设置存储桶为公共可读
                String policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}";
                
                minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                        .bucket(bucketName)
                        .config(policy)
                        .build());
                
                log.info("已创建存储桶 {} 并设置为公共可读", bucketName);
            }
            
            // 上传文件
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build());
            
            // 返回公共URL或预签名URL
            return getPreSignedUrl(bucketName, objectName);
        } catch (Exception e) {
            log.error("上传文件到MinIO失败: {}", e.getMessage(), e);
            // 降级到本地存储
            return uploadToLocalStorage(file, objectName);
        }
    }

    /**
     * 降级方案：上传到本地文件系统
     */
    private String uploadToLocalStorage(MultipartFile file, String objectName) throws IOException {
        log.info("使用本地存储上传文件: {}", objectName);
        
        // 确保目录存在
        String dirPath = localStoragePath;
        if (objectName.contains("/")) {
            dirPath = localStoragePath + "/" + objectName.substring(0, objectName.lastIndexOf("/"));
        }
        
        Path directory = Paths.get(dirPath);
        Files.createDirectories(directory);
        
        // 保存文件
        Path targetPath = Paths.get(localStoragePath, objectName);
        Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);
        
        // 返回访问URL
        return "/uploads/" + objectName;
    }

    @Override
    public void ensureBucketExists(String bucketName) throws IOException {
        if (!ensureMinioClient()) {
            // 本地存储模式下，确保目录存在
            Files.createDirectories(Paths.get(localStoragePath));
            return;
        }
        
        try {
            boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!found) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                log.info("创建桶成功: {}", bucketName);
            }
        } catch (Exception e) {
            log.error("检查或创建桶失败: {}", e.getMessage(), e);
            throw new IOException("检查或创建桶失败: " + e.getMessage(), e);
        }
    }

    @Override
    public boolean objectExists(String objectName) {
        if (!ensureMinioClient()) {
            // 检查本地文件是否存在
            Path filePath = Paths.get(localStoragePath, objectName);
            return Files.exists(filePath);
        }
        
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(minioConfig.getBucket())
                            .object(objectName)
                            .build());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public boolean objectExists(String bucketName, String objectName) {
        if (!ensureMinioClient()) {
            // 检查本地文件是否存在
            Path filePath = Paths.get(localStoragePath, objectName);
            return Files.exists(filePath);
        }
        
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public void deleteObject(String objectName) throws IOException {
        deleteObject(objectName, minioConfig.getBucket());
    }

    @Override
    public void deleteObject(String objectName, String bucketName) throws IOException {
        if (!ensureMinioClient()) {
            // 删除本地文件
            Path filePath = Paths.get(localStoragePath, objectName);
            if (Files.exists(filePath)) {
                Files.delete(filePath);
            }
            return;
        }
        
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
        } catch (Exception e) {
            log.error("删除对象失败: {}", e.getMessage(), e);
            throw new IOException("删除对象失败: " + e.getMessage(), e);
        }
    }

    @Override
    public InputStream getObject(String objectName) throws IOException {
        if (!ensureMinioClient()) {
            // 从本地文件系统读取
            Path filePath = Paths.get(localStoragePath, objectName);
            if (Files.exists(filePath)) {
                return Files.newInputStream(filePath);
            }
            throw new IOException("文件不存在: " + objectName);
        }
        
        try {
            GetObjectResponse response = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(minioConfig.getBucket())
                            .object(objectName)
                            .build());
            return response;
        } catch (Exception e) {
            log.error("获取对象失败: {}", e.getMessage(), e);
            throw new IOException("获取对象失败: " + e.getMessage(), e);
        }
    }

    @Override
    public InputStream getObject(String bucketName, String objectName) throws IOException {
        if (!ensureMinioClient()) {
            // 从本地文件系统读取
            Path filePath = Paths.get(localStoragePath, objectName);
            if (Files.exists(filePath)) {
                return Files.newInputStream(filePath);
            }
            throw new IOException("文件不存在: " + objectName);
        }
        
        try {
            GetObjectResponse response = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
            return response;
        } catch (Exception e) {
            log.error("获取对象失败: {}", e.getMessage(), e);
            throw new IOException("获取对象失败: " + e.getMessage(), e);
        }
    }

    @Override
    public String uploadAvatar(MultipartFile file, Long userId, String filename) throws IOException {
        String objectName = "avatars/" + userId + "/" + filename;
        return uploadFile(file, objectName);
    }

    @Override
    public String getFileUrl(String bucketName, String objectName) {
        if (!ensureMinioClient()) {
            // 返回本地文件URL
            return "/uploads/" + objectName;
        }
        
        // 生成短URL（不使用预签名URL）
        return minioConfig.getEndpoint() + "/" + bucketName + "/" + objectName;
        
        // 使用域名而非IP地址可以进一步缩短URL
        // return "https://minio.example.com/" + bucketName + "/" + objectName;
    }

    // 替换现有的 testConnection 方法
    private boolean testConnection() {
        try {
            // 通过列出桶来测试连接
            minioClient.listBuckets();
            log.info("MinIO连接测试成功，能够列出桶");
            return true;
        } catch (ErrorResponseException e) {
            // 凭证错误或权限问题
            log.error("MinIO连接测试失败: 凭证错误或权限不足 - {}", e.getMessage(), e);
            return false;
        } catch (InsufficientDataException e) {
            // 数据不足
            log.error("MinIO连接测试失败: 服务器返回数据不足 - {}", e.getMessage(), e);
            return false;
        } catch (InternalException e) {
            // MinIO内部错误
            log.error("MinIO连接测试失败: MinIO内部错误 - {}", e.getMessage(), e);
            return false;
        } catch (InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | 
                 ServerException | XmlParserException | IllegalArgumentException e) {
            // 其他可能的错误
            log.error("MinIO连接测试失败: {} - {}", e.getClass().getSimpleName(), e.getMessage(), e);
            return false;
        }
    }

    /**
     * 设置桶为公共可读
     * @param bucketName 桶名称
     * @throws IOException 如果设置失败
     */
    public void setBucketPolicy(String bucketName) throws IOException {
        if (!ensureMinioClient()) {
            return;
        }
        
        try {
            // 创建一个策略，允许公共读取访问
            String policy = "{\n" +
                    "    \"Version\": \"2012-10-17\",\n" +
                    "    \"Statement\": [\n" +
                    "        {\n" +
                    "            \"Effect\": \"Allow\",\n" +
                    "            \"Principal\": {\"AWS\": [\"*\"]},\n" +
                    "            \"Action\": [\"s3:GetObject\"],\n" +
                    "            \"Resource\": [\"arn:aws:s3:::" + bucketName + "/*\"]\n" +
                    "        }\n" +
                    "    ]\n" +
                    "}";
            
            minioClient.setBucketPolicy(
                    SetBucketPolicyArgs.builder()
                            .bucket(bucketName)
                            .config(policy)
                            .build());
            
            log.info("成功设置桶 {} 为公共可读", bucketName);
        } catch (Exception e) {
            log.error("设置桶策略失败: {}", e.getMessage(), e);
            throw new IOException("设置桶策略失败: " + e.getMessage(), e);
        }
    }

    /**
     * 生成预签名URL用于访问对象
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @param expirySeconds URL有效期(秒)
     * @return 预签名URL
     */
    @Override
    public String generatePresignedUrl(String bucketName, String objectName, int expirySeconds) {
        if (!ensureMinioClient()) {
            // 本地存储模式，直接返回本地路径
            return "/uploads/" + objectName;
        }
        
        try {
            return minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(io.minio.http.Method.GET)  // 使用完整路径
                            .bucket(bucketName)
                            .object(objectName)
                            .expiry(expirySeconds, TimeUnit.SECONDS)
                            .build());
        } catch (Exception e) {
            log.error("生成预签名URL失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 获取预签名URL用于访问文件
     */
    @Override
    public String getPreSignedUrl(String objectName) {
        return getPreSignedUrl(minioConfig.getBucket(), objectName);
    }

    @Override
    public String getPreSignedUrl(String bucketName, String objectName) {
        try {
            return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(7, TimeUnit.DAYS) // URL有效期为7天
                    .build());
        } catch (Exception e) {
            log.error("获取预签名URL失败: {}", e.getMessage(), e);
            throw new RuntimeException("生成访问链接失败", e);
        }
    }

    @Override
    public boolean deleteFile(String objectName) {
        return deleteFile(minioConfig.getBucket(), objectName);
    }

    @Override
    public boolean deleteFile(String bucketName, String objectName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
            return true;
        } catch (Exception e) {
            log.error("删除文件失败: {}", e.getMessage(), e);
            return false;
        }
    }

    @Override
    public List<String> listFiles(String prefix) {
        List<String> files = new ArrayList<>();
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                    .bucket(minioConfig.getBucket())
                    .prefix(prefix)
                    .recursive(true)
                    .build());
            
            for (Result<Item> result : results) {
                Item item = result.get();
                files.add(item.objectName());
            }
        } catch (Exception e) {
            log.error("列出文件失败: {}", e.getMessage(), e);
        }
        return files;
    }

    /**
     * 清理测试文件
     * @param prefix 文件前缀，例如"test/"或"temp/"
     * @return 清理的文件数量
     */
    @Override
    public int cleanupTestFiles(String prefix) {
        return cleanupTestFiles(minioConfig.getBucket(), prefix);
    }

    /**
     * 清理指定桶中的测试文件
     * @param bucketName 桶名称
     * @param prefix 文件前缀，例如"test/"或"temp/"
     * @return 清理的文件数量
     */
    @Override
    public int cleanupTestFiles(String bucketName, String prefix) {
        AtomicInteger deletedCount = new AtomicInteger(0);
        
        // 清理本地存储的测试文件
        if (StringUtils.hasText(localStoragePath)) {
            Path localDir = Paths.get(localStoragePath, prefix);
            if (Files.exists(localDir)) {
                try {
                    // 使用Files.walk遍历目录并删除文件
                    Files.walk(localDir)
                        .sorted((p1, p2) -> -p1.compareTo(p2)) // 逆序，先删文件后删目录
                        .forEach(path -> {
                            try {
                                Files.delete(path);
                                deletedCount.incrementAndGet();
                                log.info("已删除本地测试文件: {}", path);
                            } catch (IOException e) {
                                log.error("删除本地文件失败: {}", path, e);
                            }
                        });
                } catch (IOException e) {
                    log.error("遍历本地目录失败: {}", localDir, e);
                }
            }
        }
        
        // 清理MinIO中的测试文件
        if (ensureMinioClient()) {
            try {
                // 列出指定前缀的所有对象
                Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(prefix)
                        .recursive(true)
                        .build());
                
                List<String> objectsToDelete = new ArrayList<>();
                
                // 收集所有需要删除的对象名称
                for (Result<Item> result : results) {
                    try {
                        Item item = result.get();
                        objectsToDelete.add(item.objectName());
                    } catch (Exception e) {
                        log.error("获取对象信息失败: {}", e.getMessage(), e);
                    }
                }
                
                // 批量删除对象
                for (String objectName : objectsToDelete) {
                    try {
                        minioClient.removeObject(
                            RemoveObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .build());
                        deletedCount.incrementAndGet();
                        log.info("已删除MinIO测试文件: {}/{}", bucketName, objectName);
                    } catch (Exception e) {
                        log.error("删除MinIO对象失败: {}/{}", bucketName, objectName, e);
                    }
                }
            } catch (Exception e) {
                log.error("列出MinIO对象失败: {}", e.getMessage(), e);
            }
        }
        
        // 添加数据库清理代码
        try {
            int metadataDeleted = fileService.cleanupTestFileMetadata(prefix);
            log.info("已清理{}条测试文件元数据记录", metadataDeleted);
        } catch (Exception e) {
            log.error("清理测试文件元数据失败: {}", e.getMessage(), e);
        }
        
        log.info("共清理了{}个测试文件", deletedCount.get());
        return deletedCount.get();
    }

    /**
     * 清理所有测试文件(包括测试桶)
     * @return 清理的文件数量
     */
    @Override
    public int cleanupAllTestFiles() {
        AtomicInteger totalDeleted = new AtomicInteger(0);
        
        // 清理常见的测试路径
        String[] testPrefixes = {
            "test/", 
            "temp/", 
            "upload_test/", 
            "watermark_test/", 
            "sample/",
            "demo/"
        };
        
        // 清理默认桶中的测试文件
        for (String prefix : testPrefixes) {
            totalDeleted.addAndGet(cleanupTestFiles(minioConfig.getBucket(), prefix));
        }
        
        // 清理测试桶
        if (ensureMinioClient()) {
            try {
                // 清理特定的测试桶
                String[] testBuckets = {"test", "temp", "test-bucket", "sample-bucket"};
                
                for (String testBucket : testBuckets) {
                    try {
                        boolean exists = minioClient.bucketExists(
                            BucketExistsArgs.builder().bucket(testBucket).build()
                        );
                        
                        if (exists) {
                            // 首先清空桶中的所有对象
                            Iterable<Result<Item>> results = minioClient.listObjects(
                                ListObjectsArgs.builder()
                                    .bucket(testBucket)
                                    .recursive(true)
                                    .build());
                            
                            for (Result<Item> result : results) {
                                try {
                                    Item item = result.get();
                                    minioClient.removeObject(
                                        RemoveObjectArgs.builder()
                                            .bucket(testBucket)
                                            .object(item.objectName())
                                            .build());
                                    totalDeleted.incrementAndGet();
                                } catch (Exception e) {
                                    log.error("删除测试桶对象失败: {}/{}", testBucket, result, e);
                                }
                            }
                            
                            // 然后删除桶
                            minioClient.removeBucket(
                                RemoveBucketArgs.builder().bucket(testBucket).build()
                            );
                            log.info("已删除测试桶: {}", testBucket);
                        }
                    } catch (Exception e) {
                        log.error("删除测试桶失败: {}", testBucket, e);
                    }
                }
            } catch (Exception e) {
                log.error("清理测试桶过程出错: {}", e.getMessage(), e);
            }
        }
        
        log.info("共清理了{}个测试文件和测试桶", totalDeleted.get());
        return totalDeleted.get();
    }

    @Override
    public Resource loadFileAsResource(String objectName) {
        try {
            GetObjectArgs args = GetObjectArgs.builder()
                    .bucket(minioConfig.getBucket())
                    .object(objectName)
                    .build();
            
            InputStream inputStream = minioClient.getObject(args);
            
            // 创建临时文件
            Path tempFile = Files.createTempFile("download-", "-" + FilenameUtils.getName(objectName));
            Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
            inputStream.close();
            
            return new FileSystemResource(tempFile.toFile());
        } catch (Exception e) {
            log.error("从MinIO加载文件失败: {}", e.getMessage());
            throw new AppException("加载文件失败", ErrorCode.FILE_NOT_FOUND, e);
        }
    }

    @Override
    public String uploadFile(String filePath, String fileName, String objectName) throws IOException {
        if (!ensureMinioClient()) {
            return uploadLocalFileToLocalStorage(filePath, objectName);
        }
        
        try {
            // 检查存储桶是否存在，不存在则创建
            boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder()
                    .bucket(minioConfig.getBucket())
                    .build());
                    
            if (!bucketExists) {
                minioClient.makeBucket(MakeBucketArgs.builder()
                        .bucket(minioConfig.getBucket())
                        .build());
                
                // 设置存储桶为公共可读
                String policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::" + minioConfig.getBucket() + "/*\"]}]}";
                
                minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                        .bucket(minioConfig.getBucket())
                        .config(policy)
                        .build());
                
                log.info("已创建存储桶 {} 并设置为公共可读", minioConfig.getBucket());
            }
            
            // 从文件路径创建InputStream
            Path path = Paths.get(filePath);
            long fileSize = Files.size(path);
            String contentType = Files.probeContentType(path);
            if (contentType == null) {
                contentType = "application/octet-stream";
            }
            
            // 上传文件
            try (InputStream inputStream = Files.newInputStream(path)) {
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(minioConfig.getBucket())
                        .object(objectName)
                        .stream(inputStream, fileSize, -1)
                        .contentType(contentType)
                        .build());
            }
            
            // 返回公共URL或预签名URL
            return getPreSignedUrl(minioConfig.getBucket(), objectName);
        } catch (Exception e) {
            log.error("上传文件到MinIO失败: {}", e.getMessage(), e);
            // 降级到本地存储
            return uploadLocalFileToLocalStorage(filePath, objectName);
        }
    }
    
    /**
     * 降级方案：从本地文件上传到本地文件系统存储
     */
    private String uploadLocalFileToLocalStorage(String filePath, String objectName) throws IOException {
        log.info("使用本地存储上传文件: {} -> {}", filePath, objectName);
        
        // 确保目录存在
        String dirPath = localStoragePath;
        if (objectName.contains("/")) {
            dirPath = localStoragePath + "/" + objectName.substring(0, objectName.lastIndexOf("/"));
        }
        
        Path directory = Paths.get(dirPath);
        Files.createDirectories(directory);
        
        // 保存文件
        Path sourcePath = Paths.get(filePath);
        Path targetPath = Paths.get(localStoragePath, objectName);
        Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
        
        // 返回访问URL
        return "/uploads/" + objectName;
    }
} 